﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Face_Recognition
{
    class Clustering
    {
        List<Hole> holes = new List<Hole>();
   
        bool[,] skinMask;

        public List<Hole> Holes
        {
            get { return holes; }
            set { holes = value; }
        }
        List<Cluster> clusters = new List<Cluster>();

        public Clustering(List<Hole> _holes, bool[,] _skinMask) 
        {
            holes = _holes;
            skinMask = _skinMask;
        }

        public void MakeClusters()
        {
            Cluster tempCluster = new Cluster();
            Cluster tempNeighbourCluster = new Cluster();
            Hole tempHole;
            bool clustered = false;
            bool neighbourClustered = false;

            holes = holes.Where(hol => hol.Proportion >= 1 && hol.Proportion <= 3).ToList();

            foreach (Hole h in holes)
            {
                tempHole = FindClosest(h);

                foreach (Cluster c in clusters)
                {
                    if (c.Holes.Contains(h))
                    {
                        clustered = true;
                        tempCluster = c;
                        break;
                    }

                    if (c.Holes.Contains(tempHole))
                    {
                        neighbourClustered = true;
                        tempNeighbourCluster = c;
                        break;
                    }
                }

                if (!clustered)
                {
                    if (neighbourClustered)
                        tempNeighbourCluster.AddHole(h);
                    else
                    {
                        Cluster newCluster = new Cluster();
                        newCluster.AddHole(h);
                        newCluster.AddHole(tempHole);
                        clusters.Add(newCluster);
                    }
                }

                //tempCluster.AddHole(h);
                //tempCluster.AddHole(h);
            }

        }

        public List<Rectangle> FindFaces()
        {
            List<Rectangle> rectangles = new List<Rectangle>();

            holes = holes.Where(hol => hol.Proportion >= 0.75 && hol.Proportion <= 3).ToList();

            float d1, d2, d3;

            foreach (Hole h1 in holes)
            {
                foreach (Hole h2 in holes)
                {
                    d1 = h2.FindDistance(h1);
                    if (!h2.Equals(h1))
                    {
                        foreach (Hole h3 in holes)
                        {
                            if (!h1.Equals(h3) && !h2.Equals(h3))
                            {
                                d2 = h1.FindDistance(h3);
                                d3 = h2.FindDistance(h3);
                                if (d1 > 10 && d2 > 10 && d3 > 10)
                                {
                                    if (d1 / d2 > 0.5 && d1 / d2 < 1.5 &&
                                       d1 / d3 > 0.5 && d1 / d3 < 1.5 &&
                                       d3 / d2 > 0.5 && d3 / d2 < 1.5)
                                        {
                                        Cluster cluster = new Cluster();
                                        cluster.AddHole(h1);
                                        cluster.AddHole(h2);
                                        cluster.AddHole(h3);

                                        cluster.BoundCluster();

                                        if (cluster.CheckDownApex(skinMask))
                                        {
                                            //clusters.Add(cluster);
                                            bool fitter = true;

                                            for (int i=0; i < clusters.Count; i++ )
                                            {
                                                if (!clusters[i].Equals(cluster) && cluster.Intersects(clusters[i]))
                                                {
                                                    if (!cluster.IsFitter(clusters[i]))
                                                    {
                                                        fitter = false;
                                                        break;
                                                    }
                                                    else
                                                    {
                                                        clusters.Remove(clusters[i]);
                                                    }
                                                }
                                             
                                            }

                                         if(fitter)
                                                clusters.Add(cluster);
                                        }
                                            
                                    }
                                }
                            }
                        }
                    }
                }
            }

            foreach (Cluster c in clusters)
            {
                rectangles.Add(c.BoundingBox);
            }

            return rectangles;
        }

        private Hole FindClosest(Hole hole)
        {
            Hole closest;

            if (!hole.Equals(holes[0]))
                closest = holes[0];
            else
                closest = holes[1];

            foreach (Hole h1 in holes)
            {
                if (!h1.Equals(hole) && hole.FindDistance(h1) < hole.FindDistance(closest))
                {
                    closest = h1;
                }
            }

            return closest;
        }

        public Rectangle FindBoundBox()
        {
            Rectangle tempRectangle = new Rectangle();
            Rectangle boundingBox = BoundCluster(clusters[0]);
            double ratio = boundingBox.Width / boundingBox.Height ;

            foreach (Cluster c in clusters)
            {
                tempRectangle = BoundCluster(c);
                if( Math.Abs( 1 - (tempRectangle.Width / tempRectangle.Height)) < Math.Abs(1 - ratio) )
                {
                    boundingBox = Morphology.DeepClone<Rectangle>(tempRectangle);
                    ratio = tempRectangle.Width / tempRectangle.Height;
                }
            }

            return boundingBox;
        }

        private Rectangle BoundCluster(Cluster c)
        {
        
            int xmin, xmax, ymin, ymax;
                byte counter=0;
                int[] point = new int[2];
                int[] oldPoint = new int[2];
                point = new int[2]{c.Holes[0].Center.X,c.Holes[0].Center.Y} ;
                xmin = point[0];
                xmax = point[0];
                ymin = point[1];
                ymax = point[1];

            foreach(Hole h in c.Holes)
            {
                //while (holes.Count > 0)
                {
                     point = new int[2]{h.Center.X,h.Center.Y} ;
                    if (point[0] > xmax)
                        xmax = point[0];
                    if (point[0] < xmin)
                        xmin = point[0];
                    if (point[1] > ymax)
                        ymax = point[1];
                    if (point[1] < ymin)
                        ymin = point[1];

                }
            }
                return new Rectangle(2*xmin, 2*ymin, 2*(xmax - xmin), 2*(ymax - ymin));
        
        }
      
    }
}
